package com.diandi.messages.sign;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.security.*;
import java.security.cert.Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * <p>RSA签名,加解密处理核心文件，注意：密钥长度1024</p>
 * @author leelun
 * @version $Id: RSA.java, v 0.1 2013-11-15 下午2:33:53 lilun Exp $
 */
public class RSA {

    /**
     * 签名算法
     */
    public static final String  SIGNATURE_ALGORITHM = "SHA1withRSA";
    /**
     * 加密算法RSA
     */
    public static final String  KEY_ALGORITHM       = "RSA";
    /**
     * RSA最大加密明文大小
     */
    private static final int    MAX_ENCRYPT_BLOCK   = 117;

    /**
     * RSA最大解密密文大小
     */
    private static final int    MAX_DECRYPT_BLOCK   = 128;

    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY          = "RSAPublicKey";

    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY         = "RSAPrivateKey";

    /**
     * <p>
     * 生成密钥对(公钥和私钥)
     * </p>
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> genKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(1024);
        KeyPair keyPair = keyPairGen.generateKeyPair();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;

    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> genKeyPair = genKeyPair();

        String base64publicKey = getPublicKey(genKeyPair);
        System.out.println("公钥 \n" + base64publicKey);
        String base64privateKey = getPrivateKey(genKeyPair);
        System.out.println("私钥\n" + base64privateKey);

        String passwd = "cat123113";
        String charsetName = "utf-8";

        String encryptByPublicKey = Base64.encodeBase64String((encryptByPublicKey(
            passwd.getBytes(charsetName), base64publicKey)));
        System.out.println("加密\n" + encryptByPublicKey);

        byte[] decryptByPrivateKey = decryptByPrivateKey(Base64.decodeBase64("JkyqNwIXRP6Awb9u4WoCMHqoGp+PVwrmFHismagWuwyOGaAhmI2wOFRrndK4qrapt/b0cImpiIZlF0HeH9f9bQcKtjeRlClMzwu67MqXv+OgcwJAjn0Af1UtV8xUuLndiQ5CTc25784LVVaRhtFQXxRAvSDmlLMH0JsWXPv00yE="),
            "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIGm543B4xth1v4IyXx1Q2/mDlMdgsP1nfdwAwDgY2OY9ixtw1AvI7lBKUs2OlsLCim3tvmUU/xMYtS6zjHQR3pLhZ30qY3Do0tuzM0Samk/0deVP2xPj9KEvQKlUXih8VLw7+BfAJMmNhknQe8rcYFNI/YL1aWKlOlZD71Am6B7AgMBAAECgYAgbyugN+/jqr9I/9O/8enz9V6CBnDvKuPgwcHOl1Pl159GEBN7XL+YGagF68P/gKyDMTVyDFxSUp2q0CzDI3Kt1c4oyd/THT1+Z5BzQMs/TzRGeGirGWp5B40nuuYldAd/A3ngSD1nM1B9azz8NIUyHGYN25/AG7D7OpZyqV42CQJBANpXg2//q+8wDhtFARR2cpSDNrd3kImHTN9vzaMn+PJoIG8N8/9k/NtCzEojjvs3sM7qZjqM9DlxtGsCBXMVNJUCQQCYA3N5PWSFulaG94mEZtqCV1SsI9Xusz6FbfJBax7/c6oJcxBk8+1ZHhqS2YY/qY7DJQ5Q1FZi/8dWnQosGqzPAkBPTF1mPds6AZ/982/S+Lngik9DDt+ls0gOFlL/ILNfhsXQz6aAaTMUhwwfGQ8C2lUSsTrzpOHGQy+ddZx5EYiJAkBs/4x9EphRixsgC75/QYDMlPIRvGBZmAzsf7mFTtobMURrIiZPPNijXgKP6YlpfJrxMlDhhF+s/NpiMJhqp3XhAkEAvU3abcsyG3TTh8lqDxZ//ymz/PlP+kYxxad2dmUen/AhpscZLpcHzgWceG63UWYJItXFW3CvSURuzoyWF5sWMw==");
        String string = new String(decryptByPrivateKey, "utf-8");
        System.out.println("解密后\n" + "++"+string+"++");
        
        
        
        String text="_input_charset=utf-8&address=北京市东城区安定门东大街28号1号楼B单元1103号&audit_order_no=wwd_201608151436247&bank_account_no=W1XtbCt5uXEoG3Nj8zRck7hPAt7l0aM2fmQcKbUun/Gswt5G9OUmA+SBpHsoBTOD1ZMgYPnX8Pjf5qxipVaAEnXBdao7CkpHDG3A2imI8gIyge9ArsKPIHinm/QbZisL2hogDqfj9g7VWbxIKzN2FxH5iCEUSMCLdiUn4lS0jSM=&bank_branch=招商银行北京分行东方广场支行&bank_code=CMB&business_scope=技术开发；计算机系统集成服务；租赁计算机、通讯设备；设计、代理、制作、发布广告；销售电子产品等&card_attribute=B&card_type=DEBIT&cert_no=Buia4D5wCws1q7an1LO1H+Y+lpsswYXTFJ6PuHaV1qdKDcMyREB4dh4t1F1WOCJf49Fw4ZNu5u/orRHgvhOBSsPOIT3ephymKFaRZZpYcliaI3Mt63kpHsKkGWXVI4g5oLz/8PHyJQ69abS0+KzrXq3flV+kvZzizvztkxmWGb8=&cert_type=IC&city=北京市&company_name=北京茶联科技股份有限公司&digest=512a79f50783205ba3dc74b3ae3de9a4&digestType=MD5&email=DpSNLYtrJUMAqlAkxXNyvqNNF+PthVwrJK6nNJBhBnYE2Ha9H9+LkTPiPh++/4Oe4fZbgBSXUAsB0sqB/D5EjwbrRoNo0UEu2yNeDylGGjK1hB/q6rHuEsDOFn16lGVakpxMf7Wg/mtlerk5SEXMk1J+Bmh0uOy/0uzCVj4z3mY=&fileName=bjclzz.zip&identity_id=10117901&identity_type=UID&legal_person=PTGOHnPfQN/q9ahtZ2wLAyRByqLrxk2aweEpVLlbsBRPqCXx3OzRh2Cyw2YSck0Y2h5whfE4Y16BnNF2SWdrvvWvoKWLYUL7vkniGL1UaYq8MRRxrO/ZpTWki0zKnL9DHv6mgUMXl6i9p00NESmMXXpiyWwQyd7T4pqMkJ+zdaw=&legal_person_phone=SBx8iFkJrWf1M19xV9a+l16y9F4PqxA36Qpb8h+GEj1NyY6XExCgaPvs2b5/ru2XuSaWkM9eI6qI3vedtZuou4JPGm+xm9/zawX6Pws75ECdvyu9rCPjrYpL24Jz7woGn6OQG6gCMR+8Tq1MnXT5VTB/mu2Zy+nfTgBF2UIXy2g=&license_address=北京市东城区安定门东大街28号1号楼B单元1103号&license_expire_date=20991022&license_no=VnO9vMCxlMytugb0uQC3E58V6mgQukCoPwm1tCFrknqZ2Vtlpty+lpe0b+FQ6mUHSBX+OsK3S3AtTYsT7JvaL9QypEYs3at6KKsZaKkWJZs07JrDNmO4c3Sl4AxvxF6LzAjHqQ6mz58Pz3x9XqXQLqf9pEihah/Tqs6cweSv9yM=&notify_url=http://www.wowodai.cn/Weibo/notifyurl/do/audit_member_infos/uid/117901&organization_no=hIU0MH8tdpuS7hSij25Fw5kZaffkTTeP9EM0MG/0zlyk7oIWHXLSbH2Z0tA1yJ/Hl7Z90tcmxNrESJwJN8W7gGTmiveSvWv61aUNO+/6p2/LVMOCgbAN6SzbBub/Fpa1/zVdsPPkHrHDkmhkBMBhui5a9yS1wVnt6V//mwSzEKE=&partner_id=200034282989&province=北京市&request_time=20160815143624&service=audit_member_infos&summary=北京茶联科技股份有限公司成立于2011年11月，注册资本1882.93万元&telephone=SjT2t/HeZs2P6XZQgL+n5RwXw9J0AyMVp7c4H3Eol0fkg8gS16pSkVb7zN4ecjcr1EdvMz+dRDSJqUUcDFGgVzc+2zSsQrS3Jg4zb0JUNlMQ20VUmOS+Xh+QiwTf1bEDZ7XjHlmlqHR7toMdxnxZiDCBw/hG/SNfjIIdlzrnuao=&version=1.0";
        String charset="UTF-8";
        String privatekey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL0EIeDwj+YH66+jrGljzNL/NwTHs/CcyTlH14PWQV2/1LJM2mOnSqtbdhQKq3lbHD7dZ/COpJ1+ZiaZdJTubqDUa4HgqZ7aOVvZauDqHY0AEdYEUwlEdUNJAYTwx/zr62TksP1I5oFGS1MCRqlaMu0+BWkrXR0e7yyGVnuJbjg3AgMBAAECgYBC1t5AE/Fez7g5UPCcCA2bI2LKTvBDir3hgpUkarSMuVQ2hNTJXpQaCmqz5Z4DeQOA+3xQ5bAyfUsUJTnDnJkDrFBCELvKVmGL5azBCz1w0xX3iEAu5NnHEFQlEGnFCewL1cefjw7qvclFbsbXlpsxnogDe2syfkuTXys2X1oBOQJBAO/2P8YohP7CNOfRQGsFAU342MhHcivrz3UB0C4SNFvozGp3oJ9yKzlSEJWkRcSj8Or7s5Me95h7XA5xdyGO0ZsCQQDJpjNYfLb9gCmz8Wpp1lgPHxz1FiB6uIh4/xVJ7kUJYDdnWg42yaZLfv7i3y8s2i0H6l7gM9SJ6L53LmGetruVAkEAwS7VLtnGIDcHUq2yl7Xi0/wHwk1KjDN+w+sex0heoLOthfUi+dp3E+GOenpStLlrJjJW4pVmrXp6pSIRz5FkCwJAZQkObwbGFUxTGOMcY31PT/xR4Qc+LN8rGh0NSKbHFoSMHg/EUXVGCkvOYFRdacbDi1TGRuYRVywzyrmFliWO5QJAF6XlrXWN1IIXVKe9fC9r0km4m0s+KBRgi/5jxKU17PgBJZMz9pPFpxtoUHip4GsHBIoQlawPLg3eCSGiGGYb/g==";
         String publickey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXSAdHgCUqdD4lY6GvugGrlLn76lqnNNABgM6ue5ZUYicC8VFiOWnHIm2KssZmlpQnQP1weXvptTEE5Oy67SkuWpEy0UBD1AX3swLSmPgSlA0MPbe0w3U86JpSo3VlAZZbFHGjPomqHvjeG0W9nS7oF38EwpB6Oxu+KHRAqDclYwIDAQAB";
        //加签
        String str = sign(text,privatekey, charset);
        
        System.out.println("sign="+str);
        
        if(verify(text, "HmImjvMi9jglJb7U9uDiwXmOBF4rgG5FAavyIDJOVca971cpJOtgeFt9fINveBwGj8FtsqNa36xLAyvHVaMFYjIRZ9cesIimZHUv/9tR6NB6S5bsUQPJbQfcFwtnw2SLsSX93rag6CHgdpibpxcSD4HUACRhV3jc1DL4ew25ihA=", publickey,charset ))
        {
        	System.out.println("true");
        }
        else
        {
        	System.out.println("false");
        }

    }

    /**
     * 签名字符串
     *
     * @param text
     *            需要签名的字符串
     * @param privateKey 私钥(BASE64编码)
     *
     * @param charset
     *            编码格式
     * @return 签名结果(BASE64编码)
     */
    public static String sign(String text, String privateKey, String charset) throws Exception {

        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateK);
        signature.update(getContentBytes(text, charset));
        byte[] result = signature.sign();
        return Base64.encodeBase64String(result);

    }

    public static String sign(String text, PrivateKey privateKey, String charset)
                                                                                 throws SignatureException,
                                                                                 InvalidKeyException {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(privateKey);
            signature.update(getContentBytes(text, charset));
            byte[] result = signature.sign();
            return Base64.encodeBase64String(result);
        } catch (NoSuchAlgorithmException e) {
            //不可能发生，
            return null;
        }
    }

    /**
     * 签名字符串
     *
     * @param text
     *            需要签名的字符串
     * @param sign
     *            客户签名结果
     * @param publicKey
     *            公钥(BASE64编码)
     * @param charset
     *            编码格式
     * @return 验签结果
     */
    public static boolean verify(String text, String sign, String publicKey, String charset)
                                                                                            throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey publicK = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicK);
        signature.update(getContentBytes(text, charset));
        return signature.verify(Base64.decodeBase64(sign));

    }

    /**
     * <P>
     * 私钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
                                                                                     throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;

    }

    /**
     * <p>
     * 公钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
                                                                                   throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;

    }

    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data 源数据
     * @param publicKey 公钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(publicKey);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        // 对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;

    }

    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data 源数据
     * @param cert 证书
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, Certificate cert) throws Exception {

        // 对数据加密
        PublicKey uk = cert.getPublicKey();
        Cipher cipher = Cipher.getInstance(uk.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, uk);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;

    }

    /**
     * <p>
     * 私钥加密
     * </p>
     *
     * @param data 源数据
     * @param privateKey 私钥(BASE64编码)
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;

    }

    /**
     * @param content
     * @param charset
     * @return
     * @throws SignatureException
     * @throws UnsupportedEncodingException
     */
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
        }
    }

    /**
     * <p>
     * 获取私钥
     * </p>
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }

    /**
     * <p>
     * 获取公钥
     * </p>
     *
     * @param keyMap 密钥对
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64.encodeBase64String(key.getEncoded());
    }
    
    /** 
     * 从文件中加载私钥 
     *  
     * @param keyFileName 
     *            私钥文件名 
     * @return 是否成功 
     * @throws Exception 
     */  
    public static String loadPrivateKeyByFile() throws Exception {  
        try {
        	URL path = Thread.currentThread().getContextClassLoader().getResource("");
        	System.out.println(new FileReader(URLDecoder.decode(path.toString()+"/key/rsa_sign_private", "utf-8")));
            BufferedReader br = new BufferedReader(new FileReader(URLDecoder.decode(path.toString()+"/key/rsa_sign_private", "utf-8")));  
            String readLine = null;  
            StringBuilder sb = new StringBuilder();  
            while ((readLine = br.readLine()) != null) {  
                sb.append(readLine);  
            }  
            br.close();  
            return sb.toString();  
        } catch (IOException e) {  
        	System.out.println(e);
            throw new Exception("私钥数据读取错误，请根据私钥实际地址获取");  
        } catch (NullPointerException e) {  
            throw new Exception("私钥输入流为空");  
        }  
    }  
    
    
}
